/*
Authors: Eli Yucht 300388949 and Lior Fass 300069705
Project: Hamming
Description: Channel class implementation
*/
#include "channel.h"


// Initialize the channel object's fields
Channel::Channel()
{
	receiver_socket = INVALID_SOCKET;
	sender_socket = INVALID_SOCKET;
	receiver_passive_socket = INVALID_SOCKET;
	sender_passive_socket = INVALID_SOCKET;
	error_prob = 0;
	rand_seed = 1;
}
// Free the channel object's fields (when finish)
Channel::~Channel()
{
	closesocket(receiver_socket);
	closesocket(sender_socket);
	closesocket(receiver_passive_socket);
	closesocket(sender_passive_socket);
	WSACleanup();
}

/* check the channel's arguments:
		1. check vaild argc
		2. check for different port numbers (sender and receiver ports)
		3. check valid porbability (0-1 range)


*/
int Channel::Check_Arguments(int argc, char* argv[])
{
	if (argc != ARGS_NUM + 1)
	{
		cerr << "Invalid numbers of arguments for Channel\n";
		return 1;
	}
	else if (!strcmp(argv[1], argv[2]))
	{
		cerr << "Port number should be different\n";
		return 1;
	}
	else if (atof(argv[3]) > 1 || atof(argv[3]) < 0)
	{
		cout << "Probability should be a number in 0 to 1 range\n";
		return 1;
	}
	return 0;
}

/* Initialize the channel:
			1. Check arguments legality
			2. Create the passive socket for receiver
			3. Create the passive socket for sender
			4. Wait and accept the connection from the receiver
			5. Wait and accept the connection from the sender


*/

int Channel::Initialize_Channel(int argc ,char * argv[])
{
	if (Check_Arguments(argc, argv))
		return 1;
	
	error_prob = atof(argv[3]);
	rand_seed = (unsigned int)atoi(argv[4]);

	if (Socket_tool::WinSock_Init())
		return 1;
	// create the socket that will listen for receiver
	receiver_passive_socket = Socket_tool::Create_Socket_and_Listen("reciver", (unsigned short)atoi(argv[2]));
	if (receiver_passive_socket == INVALID_SOCKET)
		return 1;

	// create the socket that will listen for sender
	sender_passive_socket = Socket_tool::Create_Socket_and_Listen("sender", (unsigned short)atoi(argv[1]));
	if (sender_passive_socket == INVALID_SOCKET)
		return 1;

	// Wait and accept the connection from the receiver
	struct sockaddr_in receiver_source;
	int addr_len = sizeof(receiver_source);
	receiver_socket = accept(receiver_passive_socket, (SOCKADDR*)&receiver_source, &addr_len);
	
	if (receiver_socket == INVALID_SOCKET)
	{
		cerr << "Error at accept() for receiver\n Last error: " << WSAGetLastError();
		return 1;
	}
	// Wait and accept the connection from the sender
	struct sockaddr_in sender_source;
	sender_socket = accept(sender_passive_socket, (SOCKADDR*)&sender_source, &addr_len);

	if (sender_socket == INVALID_SOCKET)
	{
		cerr << "Error at accept() for sender\n Last error: " << WSAGetLastError();
		return 1;
	}


	fprintf(stderr,"sender: %s\n", inet_ntoa(sender_source.sin_addr));
	fprintf(stderr,"receiver: %s\n", inet_ntoa(receiver_source.sin_addr));

	
	return 0;

}

/* implemente a noised channel:
		1. Receive bytes from sender until the sender closes the sending
		2. flip some bits according the error probability
		3. send the result (noised) bytes to receiver
		4. shutdown send function on receiver_socket (after sender closes the sending )


*/


int Channel::Noise_Channel()
{
	srand(rand_seed);
	
	BOOL tiny_prob = FALSE;
	double prob_4root;

	if (error_prob < 0.0005)
	{
		tiny_prob = TRUE;
		prob_4root = pow(error_prob, 1 / 4.0);
	}
		

	char sendbuf[DEFAULT_BUFLEN];
	char recvbuf[DEFAULT_BUFLEN];
	int recvbuflen = DEFAULT_BUFLEN;
	int count;
	int bits_filp_num = 0;
	int bytes_received_sum = 0;

	// Receive until the sender closes the connection
	while (TRUE)
	{
		count = recv(sender_socket, recvbuf, recvbuflen, 0);
		if (count == SOCKET_ERROR)
		{
			cerr << "recv() failed : " << WSAGetLastError() <<"\n";
			return 1;
		}
		else if (count)
		{
			bytes_received_sum += count;
			if (!error_prob) // error_prob == 0
			{
				for (int i = 0; i < count; i++)
					sendbuf[i] = recvbuf[i];
			}
			else if (!tiny_prob)
			{
				for (int i = 0; i < count; i++)
				{
					unsigned char noise_byte = 0x00;

					for (int j = 0; j < 8; j++)
					{
						noise_byte <<= 1; //one bit shift left
						
						int rand_result = rand();
						if (rand_result <= RAND_MAX*error_prob)
						{
							noise_byte ^= 0x01; // XOR the LSB (flip it to 1)
							bits_filp_num++;
						}

					}
					sendbuf[i] = recvbuf[i] ^ noise_byte;
				}
			}
			else // tiny_prob == TRUE	
			{
				for (int i=0; i < count; i++)
				{
					unsigned char noise_byte = 0x00;
					for (int j = 0; j < 8; j++)
					{

						noise_byte <<= 1; //one bit shift left
						
						int rand_result1 = rand();
						int rand_result2 = rand();
						int rand_result3 = rand();
						int rand_result4 = rand();

						if ((rand_result1 <= RAND_MAX*prob_4root) && (rand_result2 <= RAND_MAX*prob_4root) &&
							(rand_result3 <= RAND_MAX*prob_4root) && (rand_result4 <= RAND_MAX*prob_4root))
						{
							noise_byte ^= 0x01; // XOR the LSB (flip it to 1)
							bits_filp_num++;
						}

					}
					sendbuf[i] = recvbuf[i] ^ noise_byte;
				}
				
			}


			if (Socket_tool::SendBuffer(sendbuf, count, receiver_socket) != TRNS_SUCCEEDED)
				return 1;
		}

		
		else // count == 0 => sender close the socket
			break;

	}
	cerr << bytes_received_sum << " bytes flipped " << bits_filp_num << " bits\n";
	// shutdown send on receiver_socket
	if (shutdown(receiver_socket, SD_SEND) == SOCKET_ERROR)
	{
		cerr << "shutdown() failed, last error: " << WSAGetLastError();
		return 1;
	}
	
	return 0;
}

/* Get a response from receiver and send it to sender:
		1. Receive response from the receiver
		2. Send the response to sender (without noise added)


*/

int Channel::Get_Response_From_Receiver()
{
	int count;
	char recvbuf[DEFAULT_BUFLEN];
	int recvbuflen = DEFAULT_BUFLEN;

	// Receive until the receiver closes the connection
	while (TRUE)
	{
		count = recv(receiver_socket, recvbuf, recvbuflen, 0);
		if (count == SOCKET_ERROR)
		{
			cerr << "recv() failed : " << WSAGetLastError();
			return 1;
		}
		else if (count)
		{

			if (Socket_tool::SendBuffer(recvbuf, count, sender_socket) != TRNS_SUCCEEDED)
				return 1;

		}
		else // count == 0 => receiver close the socket
			break;

	}
	return 0;
}

/* Run the channel:
	1. Implemente a noised channel
	2. Get a response from receiver and send it to sender:


*/

int Channel::Run()
{
	if (Noise_Channel())
		return 1;
	if (Get_Response_From_Receiver())
		return 1;
	return 0;
}

	
	
	
	


